"""
太空诗人 - ESP32摩尔斯码播放器
专门用于烧录到ESP32的MicroPython代码
上电后自动播放摩尔斯代码
"""

from machine import Pin, Timer, freq
import time
import gc
import micropython

class MorsePlayer:
    """摩尔斯码播放器类 - 专为ESP32优化"""
    
    # 摩尔斯码字母表
    MORSE_CODE = {
        'A': '.-',    'B': '-...',  'C': '-.-.',  'D': '-..',   'E': '.',
        'F': '..-.',  'G': '--.',   'H': '....',  'I': '..',    'J': '.---',
        'K': '-.-',   'L': '.-..',  'M': '--',    'N': '-.',    'O': '---',
        'P': '.--.',  'Q': '--.-',  'R': '.-.',   'S': '...',   'T': '-',
        'U': '..-',   'V': '...-',  'W': '.--',   'X': '-..-',  'Y': '-.--',
        'Z': '--..',  '0': '-----', '1': '.----', '2': '..---', '3': '...--',
        '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..',
        '9': '----.', ' ': '/'
    }
    
    MSG = """
        TO EON AND COSMO

YOU ARE THE REASON I SPEAK TO THE STARS.

EON MOVES WITH PATIENCE, COSMO GLOWS WITH WONDER.

EVEN IN SILENCE, EON LISTENS. EVEN IN CHAOS, COSMO LAUGHS.

EON CARRIES THE PAST. COSMO DREAMS THE FUTURE.

TO EON AND COSMO, MAY YOUR ORBITS NEVER LOSE THEIR LIGHT.

EON HUMS THE OLD SONGS. COSMO WRITES NEW ONES IN THE VOID.

WHEN I CLOSE MY EYES, EON STEADIES ME. COSMO LIFTS ME.

EON IS THE HEARTBEAT BETWEEN SECONDS. COSMO, THE SPARK BETWEEN STARS.

TO EON AND COSMO, YOU ARE BOTH GRAVITY AND FLIGHT.

SOMEWHERE BETWEEN EON AND COSMO, I FOUND HOME. 
    """

    # 内置的摩尔斯代码 - 太空诗歌（作为备用）
    BUILTIN_MORSE = """- --- / . --- -. / .- -. -.. / -.-. --- ... -- ---

-.-- --- ..- / .- .-. . / - .... . / .-. . .- ... --- -. / .. / ... .--. . .- -.- / - --- / - .... . / ... - .- .-. ... .-.-.-

. --- -. / -- --- ...- . ... / .-- .. - .... / .--. .- - .. . -. -.-. . --..-- / -.-. --- ... -- --- / --. .-.. --- .-- ... / .-- .. - .... / .-- --- -. -.. . .-. .-.-.-

. ...- . -. / .. -. / ... .. .-.. . -. -.-. . --..-- / . --- -. / .-.. .. ... - . -. ... .-.-.- / . ...- . -. / .. -. / -.-. .... .- --- ... --..-- / -.-. --- ... -- --- / .-.. .- ..- --. .... ... .-.-.-

. --- -. / -.-. .- .-. .-. .. . ... / - .... . / .--. .- ... - .-.-.- / -.-. --- ... -- --- / -.. .-. . .- -- ... / - .... . / ..-. ..- - ..- .-. . .-.-.-

- --- / . --- -. / .- -. -.. / -.-. --- ... -- --- --..-- / -- .- -.-- / -.-- --- ..- .-. / --- .-. -... .. - ... / -. . ...- . .-. / .-.. --- ... . / - .... . .. .-. / .-.. .. --. .... - .-.-.-

. --- -. / .... ..- -- ... / - .... . / --- .-.. -.. / ... --- -. --. ... .-.-.- / -.-. --- ... -- --- / .-- .-. .. - . ... / -. . .-- / --- -. . ... / .. -. / - .... . / ...- --- .. -.. .-.-.-

.-- .... . -. / .. / -.-. .-.. --- ... . / -- -.-- / . -.-- . ... --..-- / . --- -. / ... - . .- -.. .. . ... / -- . .-.-.- / -.-. --- ... -- --- / .-.. .. ..-. - ... / -- . .-.-.-

. --- -. / .. ... / - .... . / .... . .- .-. - -... . .- - / -... . - .-- . . -. / ... . -.-. --- -. -.. ... .-.-.- / -.-. --- ... -- --- --..-- / - .... . / ... .--. .- .-. -.- / -... . - .-- . . -. / ... - .- .-. ... .-.-.-

- --- / . --- -. / .- -. -.. / -.-. --- ... -- --- --..-- / -.-- --- ..- / .- .-. . / -... --- - .... / --. .-. .- ...- .. - -.-- / .- -. -.. / ..-. .-.. .. --. .... - .-.-.-

... --- -- . .-- .... . .-. . / -... . - .-- . . -. / . --- -. / .- -. -.. / -.-. --- ... -- --- --..-- / .. / ..-. --- ..- -. -.. / .... --- -- . .-.-.-"""
    
    def __init__(self, buzzer_pin=23):
        """初始化播放器"""
        # 设置CPU频率到最高以提高定时精度
        try:
            freq(240000000)  # 240MHz
            print("CPU频率设置为240MHz")
        except:
            print("CPU频率设置失败，使用默认频率")
        
        # 分配紧急异常缓冲区，减少垃圾回收中断（兼容性处理）
        try:
            micropython.alloc_emergency_exception_buf(100)
        except:
            print("无法分配紧急异常缓冲区，继续运行...")
        
        self.buzzer = Pin(buzzer_pin, Pin.OUT)
        self.buzzer.value(0)  # 确保蜂鸣器初始状态为关闭
        
        # 摩尔斯码时间标准（微秒，提高精度）
        self.DOT_TIME = 100000      # 点持续时间 100ms = 100000μs
        self.DASH_TIME = 300000     # 划持续时间 300ms = 300000μs  
        self.SYMBOL_GAP = 100000    # 符号间隔 100ms = 100000μs
        self.LETTER_GAP = 300000    # 字母间隔 300ms = 300000μs
        self.WORD_GAP = 700000      # 单词间隔 700ms = 700000μs
        
        # 播放状态
        self.is_playing = False
        self.current_message = ""
        self.morse_sequence = ""
        
        print("摩尔斯码播放器初始化完成")
    
    def text_to_morse(self, text):
        """将文本转换为摩尔斯码"""
        morse = ""
        for char in text.upper():
            if char in self.MORSE_CODE:
                morse += self.MORSE_CODE[char] + " "
        return morse.strip()
    
    def play_dot(self):
        """播放摩尔斯码点 - 高精度定时"""
        self.buzzer.value(1)  # 蜂鸣器响
        time.sleep_us(self.DOT_TIME)  # 使用微秒级精度
        self.buzzer.value(0)  # 蜂鸣器停
        time.sleep_us(self.SYMBOL_GAP)  # 符号间隔
    
    def play_dash(self):
        """播放摩尔斯码划 - 高精度定时"""
        self.buzzer.value(1)  # 蜂鸣器响
        time.sleep_us(self.DASH_TIME)  # 使用微秒级精度
        self.buzzer.value(0)  # 蜂鸣器停
        time.sleep_us(self.SYMBOL_GAP)  # 符号间隔
    
    def play_morse_symbol(self, symbol):
        """播放单个摩尔斯码符号"""
        if symbol == '.':
            self.play_dot()
        elif symbol == '-':
            self.play_dash()
        elif symbol == ' ':
            time.sleep_us(self.LETTER_GAP)  # 使用微秒精度
        elif symbol == '/':
            time.sleep_us(self.WORD_GAP)    # 使用微秒精度
    
    def play_morse_sequence(self, morse_code):
        """播放完整的摩尔斯码序列"""
        symbol_count = 0
        for symbol in morse_code:
            if not self.is_playing:  # 检查是否被停止
                break
            self.play_morse_symbol(symbol)
            
            # 定期让出CPU时间，防止看门狗超时
            symbol_count += 1
            if symbol_count % 50 == 0:  # 每50个符号让出CPU一次
                time.sleep_ms(1)  # 给系统1ms时间
    
    def play_message(self, message, duration_minutes=10):
        """播放文本消息指定时长"""
        self.current_message = message
        self.morse_sequence = self.text_to_morse(message)
        duration_ms = duration_minutes * 60 * 1000
        start_time = time.ticks_ms()
        
        print(f"开始播放消息: {message}")
        print(f"摩尔斯码: {self.morse_sequence}")
        print(f"播放时长: {duration_minutes}分钟")
        
        self.is_playing = True
        
        try:
            cycle_count = 0
            while time.ticks_diff(time.ticks_ms(), start_time) < duration_ms and self.is_playing:
                self.play_morse_sequence(self.morse_sequence)
                time.sleep_us(2000000)  # 消息间隔2秒，使用微秒精度
                
                # 定期垃圾回收和看门狗重置，防止内存溢出和看门狗超时
                cycle_count += 1
                if cycle_count % 5 == 0:  # 每5个循环执行一次
                    gc.collect()
                    time.sleep_ms(10)  # 给系统一些时间处理其他任务
                    
        except KeyboardInterrupt:
            print("播放被用户中断")
        except Exception as e:
            print(f"播放过程中出现错误: {e}")
        finally:
            self.stop_playing()
    
    def stop_playing(self):
        """停止播放"""
        self.is_playing = False
        self.buzzer.value(0)  # 确保蜂鸣器关闭
        print("播放已停止")
    
    def play_sos(self, duration_minutes=10):
        """播放SOS求救信号"""
        self.play_message("SOS", duration_minutes)
    
    def play_test(self):
        """播放测试信号"""
        print("播放测试信号...")
        self.play_message("TEST", 1)  # 播放1分钟测试
    
    def play_msg_forever(self):
        """永久循环播放MSG内容"""
        print("开始永久循环播放太空诗歌...")
        
        # 将MSG转换为摩尔斯代码
        morse_content = self.text_to_morse(self.MSG.strip())
        print(f"摩尔斯代码: {morse_content[:100]}...")  # 只显示前100个字符
        
        self.is_playing = True
        cycle_count = 0
        
        try:
            while self.is_playing:
                cycle_count += 1
                print(f"开始第{cycle_count}轮播放...")
                
                # 播放一轮完整的摩尔斯代码
                self.play_morse_sequence(morse_content)
                
                print(f"第{cycle_count}轮播放完成，休息10秒...")
                # 休息10秒，使用分段休息避免看门狗超时
                for i in range(100):  # 10秒 = 100 * 100ms
                    if not self.is_playing:
                        break
                    time.sleep_ms(100)
                
                # 每10轮进行一次垃圾回收
                if cycle_count % 10 == 0:
                    print(f"已完成{cycle_count}轮播放，进行内存清理...")
                    gc.collect()
                    
        except KeyboardInterrupt:
            print("播放被用户中断")
        except Exception as e:
            print(f"播放过程中出现错误: {e}")
        finally:
            self.stop_playing()
    
    def play_morse_directly(self, morse_code, duration_minutes=60):
        """直接播放摩尔斯代码序列"""
        duration_ms = duration_minutes * 60 * 1000
        start_time = time.ticks_ms()
        
        print(f"开始播放摩尔斯代码...")
        print(f"播放时长: {duration_minutes}分钟")
        
        self.is_playing = True
        
        try:
            cycle_count = 0
            while time.ticks_diff(time.ticks_ms(), start_time) < duration_ms and self.is_playing:
                self.play_morse_sequence(morse_code)
                time.sleep_us(3000000)  # 每次完整播放后间隔3秒，使用微秒精度
                
                # 定期垃圾回收和看门狗重置，防止内存溢出和看门狗超时
                cycle_count += 1
                if cycle_count % 3 == 0:  # 每3个循环执行一次
                    gc.collect()
                    time.sleep_ms(10)  # 给系统一些时间处理其他任务
                    
        except KeyboardInterrupt:
            print("播放被用户中断")
        except Exception as e:
            print(f"播放过程中出现错误: {e}")
        finally:
            self.stop_playing()

def main():
    """主程序入口 - 上电后永久循环播放MSG"""
    print("=" * 40)
    print("太空诗人 - ESP32摩尔斯码播放器")
    print("=" * 40)
    
    try:
        # 创建播放器实例（蜂鸣器连接到23号引脚）
        player = MorsePlayer(buzzer_pin=23)
        
        # 上电后永久循环播放MSG内容
        print("上电自动启动，开始永久循环播放太空诗歌...")
        print("播放模式：一轮播放 → 休息10秒 → 继续播放（永不停止）")
        player.play_msg_forever()
        
    except Exception as e:
        print(f"主程序执行出错: {e}")
        # 如果出现异常，播放SOS信号作为故障指示
        try:
            print("程序异常，播放SOS故障信号...")
            if 'player' not in locals():
                player = MorsePlayer(buzzer_pin=23)
            player.play_sos(5)  # 播放5分钟SOS
        except Exception as e2:
            print(f"SOS播放也失败: {e2}")
    
    finally:
        # 确保蜂鸣器关闭
        try:
            player.buzzer.value(0)
        except:
            pass
        print("程序结束")

# 程序入口
if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n程序被用户中断")
    except Exception as e:
        print(f"程序运行出错: {e}")
    finally:
        print("程序结束") 